/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved.
 */

package autodispose;

import io.reactivex.rxjava3.annotations.Nullable;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.functions.Consumer;

/**
 * Utility class to inject handlers to certain standard autodispose-lifecycle operations.
 */
public final class AutoDisposePlugins {
    static volatile boolean fillInOutsideScopeExceptionStacktraces;
    static volatile boolean hideProxies = true;

    /**
     * Prevents changing the plugins.
     */
    static volatile boolean lockdown;

    @Nullable
    private static volatile Consumer<? super OutsideScopeException> outsideScopeHandler;

    private AutoDisposePlugins() {
    }

    /**
     * Prevents changing the plugins from then on.
     *
     * <p>This allows container-like environments to prevent client messing with plugins.
     */
    public static void lockdown() {
        lockdown = true;
    }

    /**
     * Returns true if the plugins were locked down.
     *
     * @return true if the plugins were locked down
     */
    public static boolean isLockdown() {
        return lockdown;
    }

    /**
     * getHideProxies method to return hidden proxies.
     *
     * @return the value indicating whether or not to hide proxy interfaces.
     * @see #setHideProxies(boolean)
     */
    public static boolean getHideProxies() {
        return hideProxies;
    }

    /**
     * exception handler.
     *
     * @return the value indicating whether or not to fill in stacktraces in {@link
     * OutsideScopeException}.
     */
    public static boolean getFillInOutsideScopeExceptionStacktraces() {
        return fillInOutsideScopeExceptionStacktraces;
    }

    /**
     * get scope handler.
     *
     * @return the value for handling {@link OutsideScopeException}.
     */
    @Nullable
    public static Consumer<? super OutsideScopeException> getOutsideScopeHandler() {
        return outsideScopeHandler;
    }

    /**
     * set scope.
     *
     * @param handler the consumer for handling {@link OutsideScopeException} to set, null allowed
     */
    public static void setOutsideScopeHandler(
            @Nullable Consumer<? super OutsideScopeException> handler) {
        if (lockdown) {
            throw new IllegalStateException("Plugins can't be changed anymore");
        }
        outsideScopeHandler = handler;
    }

    /**
     * setFillInOutsideScopeExceptionStacktraces : exception trace.
     *
     * @param fillInStacktrace {@code true} to fill in stacktraces in {@link OutsideScopeException}s.
     *                         {@code false} to disable them (and use them as signals only). Disabling them, if you don't
     *                         care about the stacktraces, can result in some minor performance improvements.
     */
    public static void setFillInOutsideScopeExceptionStacktraces(boolean fillInStacktrace) {
        if (lockdown) {
            throw new IllegalStateException("Plugins can't be changed anymore");
        }
        fillInOutsideScopeExceptionStacktraces = fillInStacktrace;
    }

    /**
     * set Hide proxies.
     *
     * @param hideProxies {@code true} hide proxy interfaces. This wraps all proxy interfaces in
     *                    {@link autodispose} at runtime in an anonymous instance to prevent introspection, similar
     *                    to {@link Observable#hide()}. The default is {@code true}.
     */
    public static void setHideProxies(boolean hideProxies) {
        if (lockdown) {
            throw new IllegalStateException("Plugins can't be changed anymore");
        }
        AutoDisposePlugins.hideProxies = hideProxies;
    }

    /**
     * Removes all handlers and resets to default behavior.
     */
    public static void reset() {
        setOutsideScopeHandler(null);
    }
}
